<?php
// +----------------------------------------------------------------------
// | Timelog 时间日志服务
// +----------------------------------------------------------------------
// | 主要服务于Timelog数据处理
// +----------------------------------------------------------------------
// | 错误编码头 227xxx
// +----------------------------------------------------------------------

namespace common\service;

use common\model\EntityModel;
use common\model\StepCategoryModel;
use common\model\StepModel;
use common\model\TaskMemberModel;
use common\model\TaskModel;
use common\model\TimelogModel;
use common\model\UserModel;
use common\model\WorkTimeRecordModel;
use support\ErrorCode;
use support\SingletonTrait;
use think\Hook;
use think\module\Module;

class TimelogService
{

    // 单例工具
    use SingletonTrait;

    public function __construct()
    {
    }

    /**
     * 添加事件日志计时器
     * @param $param
     * @param $userId
     * @return array|bool
     * @throws \Exception
     */
    public function addTimer($param, $userId)
    {

        if ($param['module_code'] == 'task') {
            // 判定当前任务执行人是不是我
            $taskData = model(TaskModel::class)
                ->where([
                    'id' => $param['link_id']
                ])->find();

            // 标准任务
            if (in_array($taskData['step_type'], ['step', 'step_category'])) {
                if ($taskData['executor'] != $userId) {
                    throw_strack_exception('Executor changed !', ErrorCode::EXECUTOR_CHANGED);
                }
            } else { // 非标准任务
                $taskMemberData = model(TaskMemberModel::class)
                    ->where(['task_id' => $param['link_id'], 'user_id' => $userId])->find();
                if (empty($taskMemberData)) {
                    throw_strack_exception('Executor changed !', ErrorCode::EXECUTOR_CHANGED);
                }
            }

            // 已播出的集锁定
            if (!empty($taskData['episode_id'])) {
                $episodeData = model(EntityModel::class)->where(['id' => $taskData['episode_id']])->field("json->>'$.episode_show_time' as episode_show_time")->find();
                if (!empty($episodeData['episode_show_time'])) {
                    throw_strack_exception('Episode is show !', ErrorCode::EPISODE_IS_SHOW);
                }
            }
        }

        // 判断当前 Timelog 是不是已经被当前用户启动了，并且没有完成！
        $timeLogModel = model(TimelogModel::class);
        $timeLogCount = $timeLogModel->where([
            'complete' => 'no',
            'module_id' => Module::$moduleDictData['module_index_by_code'][$param['module_code']]['id'],
            'link_id' => $param['link_id'],
            'created_by' => fill_created_by()
        ])->count();

        if ($timeLogCount > 0) {
            // 添加时间日志错误码 003
            throw_strack_exception('Time log has been started by the current user! ', ErrorCode::TIME_LOG_HAS_BEEN_STARTED);
        }
        // 查询需要暂停的用户日志
        $activeLogs = $timeLogModel->where([
            'complete' => 'no',
            'module_id' => Module::$moduleDictData['module_index_by_code'][$param['module_code']]['id'],
            'created_by' => fill_created_by()
        ])->select();
        foreach ($activeLogs as $log) {
            // 暂停其他所有当前用户的时间日志
            $paramStop = [
                'id' => $log['id'],
            ];
            $this->stopTimer($paramStop);
        }

        // 添加Timelog
        $addData = [
            'complete' => 'no',
            'module_id' => Module::$moduleDictData['module_index_by_code'][$param['module_code']]['id'],
            'link_id' => $param['link_id'],
            'start_time' => date('Y-m-d H:i:s', time())
        ];
        $resData = $timeLogModel->addItem($addData);
        if (!$resData) {
            // 添加时间日志失败错误码 002
            throw_strack_exception($timeLogModel->getError(), ErrorCode::TIME_LOG_START_FAILURE);
        }
        Hook::listen('timelog_added', $resData);
        // 返回成功数据
        return $resData;
    }


    /**
     * 累加存储任务实际工时
     * @param $param
     * @throws \Exception
     */
    protected function generateTaskActualManHour($param)
    {
        if (Module::$moduleDictData['module_index_by_id'][(int)$param['module_id']]['code'] === 'task') {
            // module_id 是否为任务

            // 1. 找出当前所有已经结束的时间日志
            $timeLogModel = model(TimelogModel::class);
            $timeLogData = $timeLogModel->field('start_time,end_time')
                ->where([
                    'complete' => 'yes',
                    'link_id' => $param['link_id'],
                    'module_id' => $param['module_id'],
                ])->select();

            $taskActualManHour = 0;
            foreach ($timeLogData as $timeLogItem) {
                if ($timeLogItem['end_time'] >= $timeLogItem['start_time']) {
                    $taskActualManHour += $timeLogItem['end_time'] - $timeLogItem['start_time'];
                }
            }

            // 换算小时
            if ($taskActualManHour > 0) {
                $taskActualManHour = $taskActualManHour / (60 * 60);
            }

            // 更新当前任务时间工时
            $taskModel = model(TaskModel::class);
            if ($taskModel->where(['id' => $param['link_id']])->count()) {
                $taskModel->where(['id' => $param['link_id']])->setField('actual_man_hour', number_floor_precision($taskActualManHour, 2));
            }
        }
    }

    /**
     * 停止时间日志计数器
     * @param $param
     * @return array|bool
     * @throws \Exception
     */
    public function stopTimer($param)
    {
        $updateData = [
            'id' => $param['id'],
            'complete' => 'yes',
            'end_time' => $param["end_time"] ?? date('Y-m-d H:i:s', time())
        ];

        // 更新Timelog end_time
        $timeLogModel = model(TimelogModel::class);
        $resData = $timeLogModel->modifyItem($updateData);
        if (!$resData) {
            // 修改时间日志失败错误码 004
            throw_strack_exception($timeLogModel->getError(), ErrorCode::TIME_LOG_STOP_FAILURE);
        }
        Hook::listen('timelog_stopped', $resData);
        // 返回成功数据

        // 累加存储任务实际工时
        $this->generateTaskActualManHour($resData);

        return $resData;

    }

    /**
     * 获取个人时间日志
     * @param $userId
     * @param $moduleCode
     * @return array|mixed
     * @throws \Exception
     */
    public function getPersonalTimer($userId, $moduleCode)
    {
        $timeLogModel = model(TimelogModel::class);

        return $timeLogModel->findData(['filter' => [
            'complete' => 'no',
            'module_id' => Module::$moduleDictData['module_index_by_code'][$moduleCode]['id'],
            'created_by' => $userId
        ]]);
    }

    /**
     * 获取个人激活的时间日志
     * @return array|mixed
     * @throws \Exception
     */
    public function getPersonalTaskActiveTimer()
    {
        $timelogTaskData = $this->getPersonalTimer(fill_created_by(), 'task');

        $taskData = null;

        if (!empty($timelogTaskData['id'])) {
            // 获取当前任务信息
            $taskModel = model(TaskModel::class);

            $param['fields'] = join(',', [
                "entity.id",
                "entity.name",
                "entity.code",
                "entity.initial_episode_code",
                "entity.showings_number",
                "task.id",
                "task.name",
                "task.code",
                "task.man_hour",
                "task.frame_range",
                "task.timecode",
                "task.type",
                "task.step_type",
                "task.step_category_id",
                "task.step_id",
                "task.plan_start_time",
                "task.plan_end_time",
                "task.plan_duration",
                "task.description",
                "task.assignee",
                "task.executor",
                "task.created_by",
                "task.created",
                "task.actual_man_hour",
                "project.name",
                "project.code",
                "module.name",
                "module.code",
                "media.thumb",
                "media.md5_name",
                "media.size",
                "media.description"
            ]);

            $param['filter'] = ['task.id' => $timelogTaskData['link_id']];

            $taskData = $taskModel->findData($param);

            // 填充用户数据
            $userModel = model(UserModel::class);

            if (!empty($taskData['task']['assignee'])) {
                $taskData['assignee'] = $userModel->field('id,name,phone,union_id,avatar')->where(['id' => $taskData['task']['assignee']])->find();
            } else {
                $taskData['assignee'] = [
                    'id' => null,
                    'name' => null,
                    'phone' => null,
                    'union_id' => null
                ];
            }

            if (!empty($taskData['task']['executor'])) {
                $taskData['executor'] = $userModel->field('id,name,phone,union_id,avatar')->where(['id' => $taskData['task']['executor']])->find();
            } else {
                $taskData['executor'] = [
                    'id' => null,
                    'name' => null,
                    'phone' => null,
                    'union_id' => null
                ];
            }

            if (!empty($taskData['task']['created_by'])) {
                $taskData['created_by'] = $userModel->field('id,name,phone,union_id,avatar')->where(['id' => $taskData['task']['created_by']])->find();
            } else {
                $taskData['created_by'] = [
                    'id' => null,
                    'name' => null,
                    'phone' => null,
                    'union_id' => null
                ];
            }

            // 填充工序数据
            $stepCategoryModel = model(StepCategoryModel::class);
            if ($taskData['task']['step_type'] === 'step') {
                $stepModel = model(StepModel::class);
                if (!empty($taskData['task']['step_id'])) {
                    $taskData['step'] = $stepModel->field('id,name,code,color')->where(['id' => $taskData['task']['step_id']])->find();
                } else {
                    $taskData['step'] = [
                        'id' => null,
                        'name' => null,
                        'code' => null,
                        'color' => null
                    ];
                }
                if (!empty($taskData['task']['step_category_id'])) {
                    $taskData['step_category'] = $stepCategoryModel->field('id,name,code')->where(['id' => $taskData['task']['step_category_id']])->find();
                } else {
                    $taskData['step_category'] = [
                        'id' => null,
                        'name' => null,
                        'code' => null
                    ];
                }
            } else {
                if (!empty($taskData['task']['step_category_id'])) {
                    $taskData['step_category'] = $stepCategoryModel->field('id,name,code')->where(['id' => $taskData['task']['step_category_id']])->find();
                } else {
                    $taskData['step_category'] = [
                        'id' => null,
                        'name' => null,
                        'code' => null
                    ];
                }
            }

            $taskData['timelog'] = $timelogTaskData;
        }

        return $taskData;
    }

    /**
     * 获取任务激活的时间日志
     * @param $taskId
     * @return mixed
     * @throws \Exception
     */
    public function getTaskActiveTimer($taskId)
    {
        $timeLogModel = model(TimelogModel::class);

        $timeLogData = $timeLogModel->findData(['filter' => [
            'complete' => 'no',
            'module_id' => Module::$moduleDictData['module_index_by_code']["task"]['id'],
            'link_id' => $taskId
        ]]);

        return empty($timeLogData) ? null : $timeLogData;
    }

    /**
     * 停止激活的时间日志
     * @param $filter
     * @param $moduleCode
     * @param int $userId
     * @return bool
     * @throws \Exception
     */
    public function stopTimerByFilter($filter, $moduleCode, int $userId)
    {
        $filter['created_by'] = $userId;
        $filter['complete'] = "no";
        $filter['module_id'] = Module::$moduleDictData['module_index_by_code'][$moduleCode]['id'];
        $timeLog = model(TimelogModel::class)->where($filter)->find();
        if (empty($timeLog)) {
            return true;
        }

        $this->stopTimer([
            'id' => $timeLog['id']
        ]);

        if ($moduleCode == "task") {
            MessageService::getInstance()->notifyUserTaskStatusChanged($userId, $timeLog['link_id']);
        }
        return true;
    }

    /**
     * timelog修正
     * @param $filter
     * @param $data
     * @return array
     */
    public function timeLogCorrect($filter, $data)
    {
        // 获取到当天未完成的timelog (几点执行,待定)
        if (!empty($filter["user_id"])) {
            $unionIds = $filter["user_id"];
            unset($filter["user_id"]);

            $userList = model(UserModel::class)->where(['union_id' => ['IN', $unionIds]])->field("id,union_id")->select();

            if (empty($userList)) {
                return [];
            }


            $timeLogUserIds = [];
            foreach ($userList as $userListItem) {
                $timeLogUserIds[$userListItem['id']] = $userListItem['id'];
            }

            $timeLogUserIds = array_values($timeLogUserIds);

            $filter["created_by"] = ["IN", $timeLogUserIds];
        }

        $timeLogList = model(TimelogModel::class)->where($filter)->select();

        if (empty($timeLogList)) {
            return [];
        }

        $userIds = array_unique(array_column($timeLogList, 'created_by'));
        $userList = model(UserModel::class)->where(['id' => ['IN', $userIds]])->field("id,union_id")->select();

        $unionIdMap = array_column($userList, 'id', 'union_id');

        //获取时间映射
        $userTimeMap = [];
        foreach ($data as $dataItem) {
            if (isset($unionIdMap[$dataItem['im_user_id']])) {
//                $userTimeMap[$unionIdMap[$dataItem['im_user_id']]]["start_time"] = $userTimeMap[$unionIdMap[$dataItem['im_user_id']]]["end_time"] = 0;
                foreach ($dataItem['attendance_list'] as $attendanceList) {
                    $userCheckTime = $attendanceList["user_check_time"] / 1000;
                    $workDate = $attendanceList["work_date"] / 1000;
                    if ($attendanceList['check_type'] == "OnDuty") {
                        $userTimeMap[$unionIdMap[$dataItem['im_user_id']]][$workDate]["start_time"] = $userCheckTime;
                    }

                    if ($attendanceList['check_type'] == "OffDuty") {
                        $userTimeMap[$unionIdMap[$dataItem['im_user_id']]][$workDate]["end_time"] = $userCheckTime;
                    }
                }
            }
        }


        //获取修正的数据
        $timeLogData = [];
        $timeLogDiffKey = [];
        foreach ($timeLogList as $timeLogListItem) {
            $currentTimeToDay = strtotime(date("Y-m-d", $timeLogListItem["start_time"]));
            if (empty($userTimeMap[$timeLogListItem['created_by']][$currentTimeToDay])) {
                continue;
            }

            $userStartTime = $userTimeMap[$timeLogListItem['created_by']][$currentTimeToDay]['start_time'] ?? 0;
            $userEndTime = $userTimeMap[$timeLogListItem['created_by']][$currentTimeToDay]['end_time'] ?? 0;

            $currentTimeLogItem = $timeLogListItem;
            if (!empty($userEndTime)) {
                $this->correctTimeLog("end", $currentTimeLogItem, $timeLogListItem, $userEndTime);
            }

            if (!empty($userStartTime)) {
                $this->correctTimeLog("start", $currentTimeLogItem, $timeLogListItem, $userStartTime);
            }

            if (!empty($userEndTime) && !empty($userStartTime)) {
                $this->correctDefaultTimeLog($currentTimeLogItem, $timeLogListItem, $userStartTime, $userEndTime);
            }

            if (empty(array_diff_assoc($currentTimeLogItem, $timeLogListItem))) {
                continue;
            }

            $timeLogDiffKey = array_merge($timeLogDiffKey, array_diff_assoc($currentTimeLogItem, $timeLogListItem));
            $timeLogData[] = $currentTimeLogItem;
        }

        //只修改所需的key
        $timeLogData = array_map(function ($timeLogDataItem) use ($timeLogDiffKey) {
            $timeLogDataItemId = $timeLogDataItem['id'];
            $timeLogDataItem = array_intersect_key($timeLogDataItem, $timeLogDiffKey);
            $timeLogDataItem['id'] = $timeLogDataItemId;
            return $timeLogDataItem;
        }, $timeLogData);

        if (empty($timeLogData)) {
            return [];
        }

        model(TimelogModel::class)->saveAll($timeLogData);
        return $timeLogData;
    }

    /**
     * 时间修正处理
     * @param $type
     * @param $timeLogData
     * @param $timeLogListItem
     * @param $userWorkTime
     */
    public function correctTimeLog($type, &$timeLogData, $timeLogListItem, $userWorkTime)
    {
        switch ($type) {
            case "start":
                if ($userWorkTime < $timeLogListItem['end_time'] && $timeLogListItem['start_time'] < $userWorkTime) {
                    //如果超出上班时间则更新为上班时间
                    $timeLogData["correct_start_time"] = $userWorkTime;
                    $timeLogData["work_time_status"] = 'normal';
                }

                if (!empty($timeLogListItem['end_time']) && $timeLogListItem['end_time'] < $userWorkTime) {
                    $timeLogData['work_time_status'] = 'outside';
                }
                break;
            case "end":
                //1、task_start_time>work_end_time，更新当前日志为非正常工作时间状态  no_working
                if (!empty($timeLogListItem['start_time']) && $timeLogListItem['start_time'] > $userWorkTime) {
                    $timeLogData['work_time_status'] = 'outside';
                } elseif (empty($timeLogListItem['end_time']) || $timeLogListItem['end_time'] > $userWorkTime) {
                    // 2、task_end_time > work_end_time ,  更新correct_start_time 为work_end_time
                    // 3、task_end_time 空值
                    $timeLogData["correct_end_time"] = $userWorkTime;
                    $timeLogData["work_time_status"] = 'normal';
                }
                break;
        }

    }

    /**
     * 默认时间补齐
     * @param $timeLogData
     * @param $timeLogListItem
     * @param $userStartTime
     * @param $userEndTime
     */
    public function correctDefaultTimeLog(&$timeLogData, $timeLogListItem, $userStartTime, $userEndTime)
    {
        //判定默认时间是否在正常的上下班时间之内
        if (!empty($timeLogListItem['start_time']) && $timeLogListItem['start_time'] >= $userStartTime && $timeLogListItem['start_time'] <= $userEndTime) {
            $timeLogData["correct_start_time"] = $timeLogListItem['start_time'];
            $timeLogData["work_time_status"] = 'normal';
        }

        if (!empty($timeLogListItem['end_time']) && $timeLogListItem['end_time'] >= $userStartTime && $timeLogListItem['end_time'] <= $userEndTime) {
            $timeLogData["correct_end_time"] = $timeLogListItem['end_time'];
            $timeLogData["work_time_status"] = 'normal';
        }
    }

    /**
     * 获取当前公司中正在进行中的任务
     * @param $userId
     * @param $tenantId
     * @return array|false|mixed|string|null
     * @throws \think\Exception
     */
    public function getCurrentInProgressTask($userId, $tenantId)
    {
        $activeLogs = model(TimelogModel::class)->where([
            'complete' => 'no',
            'tenant_id' => $tenantId,
            'module_id' => module_by_code("task")['id'],
            'created_by' => $userId
        ])->find();

        $taskId = $activeLogs['link_id'];
        $entityTask = model(EntityModel::class)
            ->join("task on entity.id = task.entity_id")
            ->where(["task.id" => $taskId])
            ->field("task.id,task.entity_id,entity.name,entity.initial_episode_code,entity.showings_number,task.name as task_name")
            ->find();

        $entityTask["time_log_id"] = $activeLogs["id"];
        return $entityTask;
    }

    /**
     * 查询时间日志列表
     * @param $filter
     * @return array|false|mixed|string
     * @throws \think\Exception
     */
    public function queryTimeLogDetailData($filter)
    {
        $fields = [
            "entity.id",
            "entity.name",
            "entity.code",
            "entity.initial_episode_code",
            "entity.showings_number",
            "entity.end_time",
            "task.id", "task.name", "task.code", "task.man_hour", "task.frame_range", "task.timecode", "task.type",
            "task.step_type", "task.step_category_id", "task.step_id", "task.plan_start_time", "task.plan_end_time",
            "task.plan_duration", "task.description", "task.assignee", "task.executor", "task.created_by",
            "task.created", "task.actual_man_hour", "task.json",
            "project.name", "project.code", "project.id",
            "timelog.id", "timelog.complete", "timelog.link_id", "timelog.module_id", "timelog.tenant_id",
            "timelog.start_time", "timelog.end_time", "timelog.description", "timelog.created_by", "timelog.created",
            "task.end_time"
        ];

        $dataList = model(TimelogModel::class)
            ->join('task on task.id = timelog.link_id and timelog.module_id = ' . module_by_code('task')['id'])
            ->join('entity on entity.id = task.entity_id', 'left')
            ->join('project on project.id = task.project_id', 'left')
            ->where($filter)
            ->field(build_complex_query_fields($fields, '__'))
            ->select();
        if (empty($dataList)) {
            return [];
        }

        $taskIds = array_column($dataList, 'task__id');
        $workTimeList = model(WorkTimeRecordModel::class)
            ->where(['link_id' => ['IN', join(',', $taskIds)]])
            ->where('work_time > 0')
            ->field('sum(work_time) as work_time_sum,link_id')
            ->group('link_id')
            ->select();
        $workTimeMap = [];
        if (!empty($workTimeList)) {
            $workTimeMap = array_column($workTimeList, null, 'link_id');
        }
        foreach ($dataList as &$timelog) {
            $timelog = separate_complex_query_data($timelog, '__');
            $taskJsonArr = json_decode($timelog['task']['json'], true);
            if (!empty($taskJsonArr['is_dismantle_task'])) { // 如果是子任务
                $timelog['entity']['name'] = $taskJsonArr['dismantle_name']; // 实体显示子任务名称
            }
            unset($timelog['task']['json']);
            $workTimeSum = intval($workTimeMap[$timelog['task']['id']]['work_time_sum'] ?? 0);
            $timelog['task']['work_time_sum'] = round($workTimeSum / (60 * 60), 2);
        }
        return $dataList;
    }

    /**
     * 获取个人最后执行过的时间日志
     * @param string $moduleCode
     * @param int $userId
     * @param int $tenantId
     * @return mixed|string|null
     * @throws \think\Exception
     */
    public function getLatestExecuteTimer(string $moduleCode, int $userId, int $tenantId)
    {
        $timeLog = model(TimelogModel::class)->findData([
            'filter' => [
                'module_id' => Module::$moduleDictData['module_index_by_code'][$moduleCode]['id'],
                'created_by' => $userId,
                'tenant_id' => $tenantId,
            ],
            'order' => 'id DESC',
        ]);
        if (empty($timeLog)) {
            return null;
        }
        $detailData = $this->queryTimeLogDetailData(['timelog.id' => $timeLog['id']]);
        return $detailData[0] ?? null;

    }
}
